﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;

namespace System.IO
{
    /// <summary>Класс методов-расширений для объектов класса System.IO.DirectoryInfo</summary>
    public static class DirectoryInfoExtentions
    {
        public static FileInfo CreateFileInfo(this DirectoryInfo directory, string FileName)
        {
            FileName = FileName.Replace(':', '.');
            return new FileInfo(directory.FullName + "\\" + FileName);
        }

        /// <summary>Определить число всех вложенных файлов</summary>
        /// <param name="Directory">Исследуемая дирректория</param>
        /// <returns>Число файлов во всех вложенных поддиректориях</returns>
        [DebuggerStepThrough]
        public static long GetFilesCount(this DirectoryInfo Directory)
        {
            Contract.Requires(Directory != null);
            Contract.Ensures(Contract.Result<long>() >= 0);

            var result = 0L;
            var queue = new Queue<DirectoryInfo>();
            queue.Enqueue(Directory);
            do
            {
                var info = queue.Dequeue();
                result += info.GetFiles().Length;
                info.GetDirectories().Foreach(queue.Enqueue);
            } while(queue.Count != 0);
            return result;
        }

        /// <summary>Определить объём всех вложенных файлов включая поддиректории</summary>
        /// <param name="Directory">Исследуемая дирректория</param>
        /// <returns>Число байт всех вложенных файлов</returns>
        [DebuggerStepThrough]
        public static long GetSize(this DirectoryInfo Directory)
        {
            Contract.Requires(Directory != null);
            Contract.Ensures(Contract.Result<long>() >= 0);

            var result = 0L;
            var queue = new Queue<DirectoryInfo>();
            queue.Enqueue(Directory);
            do
            {
                var info = queue.Dequeue();
                result += info.GetFiles().Sum(f => f.Length);
                info.GetDirectories().Foreach(queue.Enqueue);
            } while(queue.Count != 0);
            return result;
        }

        /// <summary>Определить число поддерикторий</summary>
        /// <param name="Directory">Исследуемая дирректория</param>
        /// <returns>Число элементов в дереве поддерикторий</returns>
        [DebuggerStepThrough]
        public static long GetSubdirectoriesCount(this DirectoryInfo Directory)
        {
            Contract.Requires(Directory != null);
            Contract.Ensures(Contract.Result<long>() >= 0);

            var num = 0L;
            var queue = new Queue<DirectoryInfo>();
            queue.Enqueue(Directory);
            do
            {
                var directories = queue.Dequeue().GetDirectories();
                num += directories.Length;
                directories.Foreach(queue.Enqueue);
            } while(queue.Count != 0);
            return num;
        }

        /// <summary>Проверить - является ли дирректория пустой</summary>
        /// <param name="Directory">Проверяемая дирректория</param>
        /// <returns>Истина, если дирректория пуста</returns>
        [DebuggerStepThrough]
        public static bool IsEmpty(this DirectoryInfo Directory)
        {
            Contract.Requires(Directory != null);

            return ((Directory.GetDirectories().Length == 0) && (Directory.GetFiles().Length == 0));
        }

        /// <summary>Получить объект наблюдения за дирректорией</summary>
        /// <param name="directory">Наблюдаемая дирректория</param>
        /// <param name="filter">Фильтр файлов</param>
        /// <returns>Объект наблюдатель</returns>
        [DebuggerStepThrough]
        public static FileSystemWatcher GetWacher(this DirectoryInfo directory, string filter = null)
        {
            Contract.Requires(directory != null);
            Contract.Ensures(Contract.Result<FileSystemWatcher>() != null);

            return string.IsNullOrEmpty(filter)
                ? new FileSystemWatcher(directory.FullName)
                : new FileSystemWatcher(directory.FullName, filter);
        }
    }
}